#ifndef __FLOAT_IMAGE_H__
#define __FLOAT_IMAGE_H__

#include "buf2d.h"
#include "color.h"
#include <float.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>

#ifdef USE_IMAGEBMP
#include "imagebmp.h"
#endif

#ifdef FLOATIMAGE_USEIMAGE
#include "image.h"
#include "gamma.h"
#endif

#ifndef max
#define max(a, b) ((a) < (b) ? (b) : (a))
#endif

#ifndef min
#define min(a, b) ((a) < (b) ? (a) : (b))
#endif



struct gammatable
{
	float table[256];
	float gamma;
	
	gammatable() : gamma(0) { memset(table, 0, sizeof(float) * 256); }
	gammatable(float g) : gamma(0) { CreateTable(g); }

	void CreateTable(float g) {
		gamma = g;
		for (int x = 0; x < 256; x++)
			table[x] = (float) pow((float)x / 255.0f, gamma);
	}

	void InitGamma(float g) {
		if (g == gamma)
			return;
		CreateTable(g);
	}

};


class floatimage : public buf2d<fRGB>
{
public:

	char name[1024];

	floatimage()
	{
		buf = 0;
		sizex = sizey = 0;

	}

	floatimage(int nsizex, int nsizey)
	{
		buf = 0;
		sizex = sizey = 0;

		if (nsizex && nsizey)
			Allocate(nsizex, nsizey);
	}


	floatimage(const floatimage &in)
	{
		buf = 0;
		sizex = sizey = 0;

		Allocate(in.sizex, in.sizey);

		memcpy(buf, in.buf, bufbytes());
	}

	int LoadPFM(const char *filename);
	int LoadPFMCropped(const char *filename, int startX, int startY, int width, int height) ;
	int LoadRAW(const char *filename, int width, int height, int rgb, int byte_reverse, int header = 0);
	int LoadRAWBytes(const char *filename, int width, int height, int rgb, float gamma, int header = 0);
	int LoadRAWShorts(const char *filename, int width, int height, int rgb, int byte_reverse, int header = 0);
	int LoadRAWUnsignedShorts(const char *filename, int width, int height, int rgb, int byte_reverse, int header = 0);
	int LoadRAWLongs(const char *filename, int width, int height, int rgb, int byte_reverse, int header = 0);
	int LoadRAWUnsignedLongs(const char *filename, int width, int height, int rgb, int byte_reverse, int header = 0);
	int LoadRAW(const char *filename);

#ifdef FLOATIMAGE_USETIFF
	int LoadTIF(const char *filename, float image_gamma = 2.2f, int *bits = NULL);
	int LoadTIF_NoError(const char *filename, float image_gamma = 2.2f, int *bits = NULL);
	int LoadTIF_Curve(const char *filename, float rcurve[256], float gcurve[256], float bcurve[256]);
	int LoadTIFSize(const char *filename);
#endif

	int LoadBMP(const char *filename, float image_gamma = 2.2f);
	int LoadBMP_SubImage(int startx, int starty, int width, int height, const char *filename, float image_gamma = 2.2f);
	int LoadBMPSize(const char *filename);
	int LoadBMP_Curve(const char *filename, float rcurve[256], float gcurve[256], float bcurve[256]);
	int LoadPPM(const char *filename, float image_gamma = 2.2f);
	int LoadPPM_Curve(const char *filename, float rcurve[256], float gcurve[256], float bcurve[256]);
	int LoadJPG(const char *filename, float image_gamma = 2.2f);
	int LoadJPG_Curve(const char *filename, float rcurve[256], float gcurve[256], float bcurve[256]);
	int LoadWVT(const char *filename, float image_gamma);

	int FromBMP(char *pData, float image_gamma = 2.2f);
	int FromBMP_Curve(char *pData, float rcurve[256], float gcurve[256], float bcurve[256]);

#ifdef FLOATIMAGE_RGBEFILES
	int LoadHDR(const char *filename);
	int SaveHDR(const char *filename);
#endif

	int SavePFM(const char *filename);
	int SavePFM_channel(const char *filename, int channel = 2);
	int SaveRAW(const char *filename);
	int SaveTIF(const char *filename);
	int SaveBMP(const char *filename, float image_scale = 1.0f, float image_gamma = 2.2f);
	int SaveBMP_channel(const char *filename, int channel = 2, float image_scale = 1.0f, float image_gamma = 2.2f);
	int SavePPM(const char *filename, float image_scale = 1.0f, float image_gamma = 2.2f);
	int SaveJPG(const char *filename, float image_scale = 1.0f, float image_gamma = 2.2f);
	int SaveWVT(const char *filename, float image_scale, float image_gamma);

#ifdef USE_IMAGEBMP
	// Create from an imagebmp (same layout as a Windows BMP data block) in memory
	int FromRGB(const imagergb &img, float image_gamma);
	int FromRGB_Curve(const imagergb &img, float rcurve[256], float gcurve[256], float bcurve[256]);
#endif

	fRGB GetPixelBilinear(float sx, float sy);
	void HalfSize();
	void DoubleSize();
	void Clamp(float r0, float g0, float b0, float r1, float g1, float b1);
	void Quantize(int levels, float minval, float maxval);
	void gaussian_blur(int variance);
	void fast_gaussian_blur(int variance, float *percent_complete = NULL);

	void blur1x();
	void blur1y();
	void box_blur(int hw);
	void boxblurx(int hw);
	void boxblury(int hw);

	void convolve_naive(floatimage &kernel, float *percent_complete = NULL);
	void convolve_naive_simple(floatimage &kernel);

	void Scale(float cr,float cg,float cb);
	void Scale(float s);
	void shoulder(float cut);
	void vignette(float r0, float r1);
	void sepia(float cr,float cg,float cb);
	void ResampleFrom(floatimage &in);
	void SwapByteOrder();

	void AddTo(floatimage &dst, int dst_left, int dst_top, int src_left = 0, int src_top = 0, int src_width = -1, int src_height = -1);
	void BlitTo_NB(floatimage &dst, int dst_left, int dst_top, int src_left = 0, int src_top = 0, int src_width = -1, int src_height = -1);

	void ClipLower(float min, float replace);
	int CopyChannel(floatimage &other, int src_channel, int dst_channel);

#ifdef FLOATIMAGE_USEIMAGE
	void addWithGamma(image &i, float gamma = 1.75f);
	void minWithGamma(image &i, float gamma = 1.75f);
	void ToImage(image &i, float gamma = 1.75f);
#endif

	void Blend(floatimage &ia, floatimage &ib, floatimage &ic, floatimage &id);
	void pow(double p);
	void MultiplyInverse(floatimage &a, floatimage &b);
	void absolutevalue();
	void ClampRange(float min, float max);
	void minimum(floatimage &other);
	void maximum(floatimage &other);
	void find_maximum(int x0, int y0, int x1, int y1, int &maxx, int &maxy);
	void find_minimum(int x0, int y0, int x1, int y1, int &minx, int &miny);
	void find_maximum_subpixel(float &maxx, float &maxy);
	fRGB maximum(int x0, int y0, int x1, int y1);
	fRGB minimum(int x0, int y0, int x1, int y1);
	void ScaleToRange(float min, float max);
	fRGB average(int x0, int y0, int x1, int y1);
	void ToSumTable();
	fRGB SumTable_sum(int x0, int y0, int x1, int y1);
	fRGB SumTable_average(int x0, int y0, int x1, int y1);

	fRGB GetPixelBilinearSlowClip(float sx, float sy);
	fRGB GetPixelBilinearLogSlow(float sx, float sy);		// cubic, not energy-preserving, non-negative, interpolating
	fRGB GetPixelBSplineSlow(float sx, float sy);			// cubic, energy-preserving, non-negative, non-interpolating, slightly 'blurry'
	fRGB GetPixelBicubicLogSlow(float sx, float sy);
	void AddPixelBilinear(float sx, float sy, fRGB color);
	void AddPixelBilinearSlowClip(float sx, float sy,fRGB c);
	void AddPixelGaussian(float sx, float sy, float width, float alpha, fRGB c);
	void AddPixelGaussianNormalized(float sx, float sy, float width, float alpha, float N, fRGB c);
	void Gaussian(float width, float alpha, fRGB c);
	float GetGaussianNormalized(float width, float alpha);
	void MedianFilter3x3();


	void UpsampleX(int newsizex);
	void DownsampleX(int newsizex);
	void ResampleX(int newsizex);

	void GetSubImage(int xstart, int ystart, int xwidth, int yheight, floatimage &dest);

	void AddBlitModulate(int xstart, int ystart, floatimage &source, float r, float g, float b);
	void AddBlitScaleModulate(int xstart, int ystart, float scale, floatimage &source, float r, float g, float b);
	void AddBlitScaleFastSubPixel(float xstart, float ystart, float scale, floatimage &source);
	void AddBlitScale(int xstart, int ystart, float scale, floatimage &source);
	void AddBlitScaleFast(int xstart, int ystart, float scale, floatimage &source);

	void ShiftX(float percent, floatimage &dest);

	int CountColor(float r, float g, float b);
	void NormalizeToMax();


	floatimage HalfSizeDeleteOld();


};

int LoadBMPChannel(buf2d<float> &fchannel, const char *filename);
int LoadBMPLine(floatimage &fimg, int line, const char *filename);


#endif